package algorithms.fuzzyKMeans;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import algorithms.distanceFunctions.Distance;
import dataSet.Membership;
import dataSet.MembershipMatrix;
import dataSet.Nop;
import dataSet.Topic;

public class FuzzyKMeans
{

	private List<Nop>			nopList;
	private int					clustersNumber;
	private List<Nop>			centersList;
	private List<Membership>	membershipOfNop;
	private MembershipMatrix	membershipMatrix;

	private Set<Topic>			topicList;

	private Double				m;					// fuzzyFactor
	private Double				e;					// a termination criterion

	// between 0 and 1

	public void initialize(List<Nop> nopList, int clustersNumber, Double fuzzyFactor, Double e)
	{
		this.m = fuzzyFactor;
		this.e = e;
		this.nopList = nopList;
		topicList = this.nopList.get(0).getTopicWeightMap().keySet();
		this.clustersNumber = clustersNumber;

		// initialize centers
		centersList = new ArrayList<Nop>();
		List<Nop> tempCentersList = new ArrayList<Nop>();
		for (int i = 0; i < this.clustersNumber;)
		{
			int indexOfNop = (int) (Math.random() * nopList.size());
			if (!tempCentersList.contains(nopList.get(indexOfNop)))
			{
				tempCentersList.add(nopList.get(indexOfNop));
				i++;
			}
		}
		for (int i = 0; i < this.clustersNumber; i++)
		{
			Nop aNewNop = new Nop();
			for (Topic aTopic : this.topicList)
			{
				aNewNop.setTopicWeightMap(aTopic, tempCentersList.get(i).getTopicWeight(aTopic));
			}
			this.centersList.add(aNewNop);
		}
		// initialize MembershipList
		this.membershipOfNop = new ArrayList<Membership>();
		this.initializeMembershipOfNop();
		// System.out.println("the length of MM is : \t" +
		// this.membershipOfNop.size());

		// for (Nop aNop : this.nopList) {
		// for (Nop aCenter : this.centersList) {
		// Membership aMembership = new Membership();
		// aMembership.setNop(aNop);
		// aMembership.setCenter(aCenter);
		// aMembership.setMembershipValue(0.0);
		// this.membershipOfNop.add(aMembership);
		// }
		// }
		// this.membershipMatrix = new MembershipMatrix();
		// for (Nop bNop : this.nopList) {
		// this.membershipMatrix.setMembershipWithMembershipsOfNop(bNop,
		// this.membershipOfNop);
		// }
	}

	public void mainLoop()
	{
		for (int i = 0; i < 5; i++)
		{
			this.updateMembership();
		}
	}

	public void updateMembershipMatrix()
	{
		Double distanceNopToCenter_i = 0.0;
		Double distanceNopToCenter_j = 0.0;
		Double membershipValue = 0.0;

		for (Nop aNop : this.nopList)
		{

			this.membershipOfNop = new ArrayList<Membership>();
			initializeMembershipOfNop();

			for (int i = 0; i < this.clustersNumber; i++)
			{
				Nop center_i = this.centersList.get(i);
				distanceNopToCenter_i = Distance.euclideanDistance(aNop, center_i);

				if (distanceNopToCenter_i == 0.0)
				{
					for (int l = 0; l < this.clustersNumber; l++)
					{
						if (l == i)
						{
							membershipValue = 1.0;
							center_i = this.centersList.get(l);
							this.membershipOfNop.get(l).setCenter(center_i);
							this.membershipOfNop.get(l).setNop(aNop);
							this.membershipOfNop.get(l).setMembershipValue(membershipValue);
						}
						else
						{
							membershipValue = 0d;
							center_i = this.centersList.get(l);
							this.membershipOfNop.get(l).setCenter(center_i);
							this.membershipOfNop.get(l).setNop(aNop);
							this.membershipOfNop.get(l).setMembershipValue(membershipValue);
						}
					}
					this.membershipMatrix.setMembershipWithMembershipsOfNop(aNop, membershipOfNop);
					break;
				}
				else
				{
					for (int j = 0; j < this.clustersNumber; j++)
					{
						distanceNopToCenter_j = Distance.euclideanDistance(aNop, this.centersList.get(j));
						membershipValue += Math.pow((distanceNopToCenter_i / distanceNopToCenter_j), (2 / (m - 1)));
					}

					this.membershipOfNop.get(i).setNop(aNop);
					this.membershipOfNop.get(i).setCenter(center_i);
					this.membershipOfNop.get(i).setMembershipValue(membershipValue);

					this.membershipMatrix.setMembershipWithMembershipsOfNop(aNop, membershipOfNop);

					membershipValue = 0d;
				}
			}
			this.membershipMatrix.setMembershipWithMembershipsOfNop(aNop, membershipOfNop);
		}
	}

	private void initializeMembershipOfNop()
	{
		for (Nop aNop : this.nopList)
		{
			for (Nop aCenter : this.centersList)
			{
				Membership aMembership = new Membership();
				aMembership.setNop(aNop);
				aMembership.setCenter(aCenter);
				aMembership.setMembershipValue(0.0);
				this.membershipOfNop.add(aMembership);
			}
		}

	}

	public void updateMembership()
	{
		Double distanceNopToCenter_i = 0.0;
		Double distanceNopToCenter_j = 0.0;
		Double membershipValue = 0.0;
		for (Nop aNop : this.nopList)
		{

			for (int i = 0; i < this.clustersNumber; i++)
			{

				distanceNopToCenter_i = Distance.euclideanDistance(aNop, this.centersList.get(i));

				if (distanceNopToCenter_i == 0.0)
				{
					for (int l = 0; l < this.clustersNumber; l++)
					{
						if (l == i)
						{
							membershipValue = 1.0;
							int indexOfMembership = this.findMembership(aNop, this.centersList.get(l));
							// System.out.println(this.nopList.indexOf(aNop) +
							// "\t" + i);
							if (!(indexOfMembership == -1))
							{
								this.membershipOfNop.get(indexOfMembership).setMembershipValue(membershipValue);
							}
							else
							{
								System.out.println("some this is wrong.");
							}
						}
						else
						{
							membershipValue = 0d;
							int indexOfMembership = this.findMembership(aNop, this.centersList.get(l));
							if (!(indexOfMembership == -1))
							{
								this.membershipOfNop.get(indexOfMembership).setMembershipValue(membershipValue);
							}
							else
							{
								System.out.println("some this is wrong.");
							}
						}
					}
					break;
				}
				else
				{
					for (int j = 0; j < this.clustersNumber; j++)
					{
						distanceNopToCenter_j = Distance.euclideanDistance(aNop, this.centersList.get(j));
						membershipValue += Math.pow((distanceNopToCenter_i / distanceNopToCenter_j), (2 / (m - 1)));
					}
					int indexOfMembership = this.findMembership(aNop, this.centersList.get(i));
					if (!(indexOfMembership == -1))
					{
						this.membershipOfNop.get(indexOfMembership).setMembershipValue(1 / membershipValue);

					}
					else
					{
						System.out.println("some this is wrong.");
					}

					membershipValue = 0d;
				}
			}
		}
	}

	public void updateCenters()
	{
		// sum of u(ij)^m
		Double sumOfMembership = 0.0;
		// sum of u(ij)^m * x(i)
		Double sumOfValue;
		// new center : sumOfValue / sumOfMembership
		Nop tempNop;
		for (Nop aNop : this.nopList)
		{
			for (Nop aCenter : this.centersList)
			{
				int indexOfMembership = this.findMembership(aNop, aCenter);
				if (!(indexOfMembership == -1))
				{
					sumOfMembership += Math.pow(this.membershipOfNop.get(indexOfMembership).getMembershipValue(), m);
				}
			}
		}
		System.out.println("sumOfMembership " + sumOfMembership);
		//

		for (Nop aCenter : this.centersList)
		{
			tempNop = new Nop();
			for (Topic aTopic : this.topicList)
			{
				tempNop.setTopicWeightMap(aTopic, 0.0);
			}
			for (Topic aTopic : topicList)
			{
				sumOfValue = 0.0;
				for (Nop aNop : this.nopList)
				{
					int indexOfMembership = this.findMembership(aNop, aCenter);
					if (!(indexOfMembership == -1))
					{

						double u = this.membershipOfNop.get(indexOfMembership).getMembershipValue();
						double x = aNop.getTopicWeight(aTopic);
						double temp = Math.pow(u, m) * x;

						sumOfValue += temp;

					}
					else
					{
						System.out.println("----------error-----------");
					}
				}
//				System.out.println("sumOfValue " + sumOfValue);
				double newWeight = sumOfValue / sumOfMembership;
				tempNop.setTopicWeightMap(aTopic, newWeight);
			}
			for (Topic aTopic : this.topicList)
			{
				double newWeight = tempNop.getTopicWeight(aTopic);
				aCenter.setTopicWeightMap(aTopic, newWeight);
			}
		}
	}

	public int findMembership(Nop aNop, Nop aCenter)
	{
		for (Membership aMembership : this.membershipOfNop)
		{
			if ((aMembership.getNop() == aNop) && (aMembership.getCenter() == aCenter)) { return this.membershipOfNop.indexOf(aMembership); }
		}
		return -1;
	}

	public List<Membership> getMembershipList()
	{
		return this.membershipOfNop;
	}

	public List<Nop> getCenterList()
	{
		return this.centersList;
	}

	public void showCenterList()
	{

		for (Nop aNop : this.centersList)
		{
			for (Topic aTopic : topicList)
			{
				System.out.printf("%4.2f\t", aNop.getTopicWeight(aTopic));
			}
			System.out.println();
		}
	}

	public void testMembershipSum()
	{
		Nop aNop = this.nopList.get(0);
		Double sum = 0d;
		for (Nop aCenter : this.centersList)
		{
			for (Membership aMembership : this.membershipOfNop)
			{
				if ((aMembership.getNop() == aNop) && (aMembership.getCenter() == aCenter))
				{
					sum += this.membershipOfNop.get(this.membershipOfNop.indexOf(aMembership)).getMembershipValue();
				}
			}
		}
		System.out.println("the sum of membership is : " + sum);
	}

	public void showMembershipList()
	{
		for (Nop aNop : this.nopList)
		{
			System.out.print("Nop " + this.nopList.indexOf(aNop) + "\t");
			for (Nop aCenter : this.centersList)
			{
				int indexOfMembership = this.findMembership(aNop, aCenter);
				if (!(indexOfMembership == -1))
				{
					System.out.printf("%4.4f\t", this.membershipOfNop.get(indexOfMembership).getMembershipValue());
				}
			}
			System.out.println();
		}
	}

	public void showMembershipMatrix()
	{
		this.membershipMatrix.showMembershipMatrix();
	}

	public List<Nop> getNopList()
	{
		return nopList;
	}

	public void setNopList(List<Nop> nopList)
	{
		this.nopList = nopList;
	}

	public int getClustersNumber()
	{
		return clustersNumber;
	}

	public void setClustersNumber(int clustersNumber)
	{
		this.clustersNumber = clustersNumber;
	}

	public List<Nop> getCentersList()
	{
		return centersList;
	}

	public void setCentersList(List<Nop> centersList)
	{
		this.centersList = centersList;
	}

	public List<Membership> getMembershipOfNop()
	{
		return membershipOfNop;
	}

	public void setMembershipOfNop(List<Membership> membershipOfNop)
	{
		this.membershipOfNop = membershipOfNop;
	}

	public MembershipMatrix getMembershipMatrix()
	{
		return membershipMatrix;
	}

	public void setMembershipMatrix(MembershipMatrix membershipMatrix)
	{
		this.membershipMatrix = membershipMatrix;
	}

	public Double getM()
	{
		return m;
	}

	public void setM(Double m)
	{
		this.m = m;
	}

	public Double getE()
	{
		return e;
	}

	public void setE(Double e)
	{
		this.e = e;
	}

}
